package com.quanz.ckgl.services.hibernate;


import java.io.Serializable;
import java.sql.Date;
import java.sql.Types;
import java.util.Calendar;
import java.util.TimeZone;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;
import org.hibernate.usertype.UserVersionType;

/**
 * UserType for non-default TimeZone. Hibernate's built-in date, time and
 * timestamp types assume that dates in the database are in Java's default time
 * zone, implicitly. If this assumption is false (and you can't make it true by
 * calling java.util.TimeZone.setDefault), you can configure Hibernate to map to
 * a UserType that does something else....
 * 
 * This code is taken from {@link http ://www.hibernate.org/100.html}. However,
 * there are comments there which apply to us, namely that it would be useful to
 * treat the activeOn property as a version field which is maintained by the
 * database and it is not clear how to get that to work. Also the thread
 * {@link http ://forum.hibernate.org/viewtopic.php?t=980279} suggests handling
 * this conversion in the POJO instead. However, at least one link (reference?)
 * I found indicated a problem when the front-end and the middle layer lived in
 * different time zones. I'm not sure that applies to use since we don't allow
 * the front end to set timestamps however, if London starts handling releases,
 * there may be a problem displaying times in an easily understood form for the
 * users.
 */
public abstract class HibernateUTC implements UserVersionType, UserType {
	/**
	 * Note 071107: passing the static sUTCCalendar instance to the
	 * setTimestamp(), getTimestamp() calls above has concurrency issues, as
	 * some JDBC drivers do modify the supplied calendar instance. Consequence,
	 * the calendar is cloned before use.
	 */
	/** the Calendar to hold the UTC timezone */
	private static final TimeZone TZ_UTC;
	static final Calendar UTC_CALENDAR;
	static {
		TZ_UTC = TimeZone.getTimeZone("UTC");
		UTC_CALENDAR = Calendar.getInstance(TZ_UTC);
	}

	/** the SQL type this type manages */
	protected static int[] SQL_TYPES_UTC = { Types.TIMESTAMP };

	/**
	 * The class of objects returned by <code>nullSafeGet</code>. Currently,
	 * returned objects are derived from this class, not exactly this class.
	 */
	protected Class<?> objectClass = Date.class;

	/**
	 * @see net.sf.hibernate.UserType#sqlTypes()
	 */
	@Override
	public int[] sqlTypes() {
		return SQL_TYPES_UTC;
	}

	/**
	 * @see net.sf.hibernate.UserType#returnedClass()
	 */
	@Override
	public Class<?> returnedClass() {
		return objectClass;
	}

	/**
	 * @see net.sf.hibernate.UserType#equals(java.lang.Object, java.lang.Object)
	 */
	@Override
	public boolean equals(Object x, Object y) {
		return (x == null) ? (y == null) : x.equals(y);
	}

	/**
	 * Get a hashcode for the instance, consistent with persistence "equality"
	 */
	@Override
	public int hashCode(Object x) throws HibernateException {
		return x.hashCode();
	}

	/**
	 * @see net.sf.hibernate.UserType#isMutable()
	 */
	@Override
	public boolean isMutable() {
		return true;
	}

	/**
	 * Transform the object into its cacheable representation. At the very least
	 * this method should perform a deep copy if the type is mutable. That may
	 * not be enough for some implementations, however; for example,
	 * associations must be cached as identifier values. (optional operation)
	 * 
	 * @param value
	 *            the object to be cached
	 * @return a cachable representation of the object
	 * @throws HibernateException
	 */
	@Override
	public Serializable disassemble(Object value) throws HibernateException {
		return (Serializable) deepCopy(value);
	}

	/**
	 * Reconstruct an object from the cacheable representation. At the very
	 * least this method should perform a deep copy if the type is mutable.
	 * (optional operation)
	 * 
	 * @param cached
	 *            the object to be cached
	 * @param owner
	 *            the owner of the cached object
	 * @return a reconstructed object from the cachable representation
	 * @throws HibernateException
	 */
	@Override
	public Object assemble(Serializable cached, Object owner)
			throws HibernateException {
		return deepCopy(cached);
	}

	/**
	 * During merge, replace the existing (target) value in the entity we are
	 * merging to with a new (original) value from the detached entity we are
	 * merging. For immutable objects, or null values, it is safe to simply
	 * return the first parameter. For mutable objects, it is safe to return a
	 * copy of the first parameter. For objects with component values, it might
	 * make sense to recursively replace component values.
	 * 
	 * @param original
	 *            the value from the detached entity being merged
	 * @param target
	 *            the value in the managed entity
	 * @return the value to be merged
	 */
	@Override
	public Object replace(Object original, Object target, Object owner)
			throws HibernateException {
		return deepCopy(original);
	}

	@Override
	public Object seed(SessionImplementor si) {
		return null;
	}

	@Override
	public Object next(Object current, SessionImplementor si) {
		return null;
	}
}